package com.yance.fim.cluster.websocket;

import com.yance.fim.utils.Constants;
import com.yance.fim.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import org.tio.core.Tio;
import org.tio.websocket.client.WebSocket;
import org.tio.websocket.client.WsClient;
import org.tio.websocket.client.config.WsClientConfig;

import java.util.Timer;
import java.util.TimerTask;

/**
 * 集群内部通信客户端创建者
 */
public class WebsocketClientClusterCreator {

    private static Logger logger = LoggerFactory.getLogger(WebsocketClientClusterCreator.class);

    private String uri;

    private ChannelContext channelContext;

    private volatile int state;

    private WebSocket webSocket = null;

    private Timer timer = null;

    private WsClient wsClient = null;

    public WebsocketClientClusterCreator() {
    }

    public WebsocketClientClusterCreator(String uri, ChannelContext channelContext) {
        this.uri = uri;
        this.channelContext = channelContext;
        creator();
        sendHeartbeatPacket();
    }

    private void creator() {
        try {
            wsClient = WsClient.create(uri, new WsClientConfig(openEvent -> {
                state = WebSocket.OPEN;
                logger.info("WebsocketCreator ClientUri[{}] Open", uri);
            }, messageEvent -> {
                logger.info("WebsocketCreator ClientUri[{}] MessageEvent", uri);
                Tio.send(channelContext, messageEvent.data);
            }, closeEvent -> {
                state = WebSocket.CLOSED;
                logger.info("WebsocketCreator ClientUri[{}] Close", uri);
                Tio.close(channelContext, "WebsocketCreator Close");
                onDestroy(true);
            }, errorEvent -> {
                state = WebSocket.CLOSED;
                logger.info("WebsocketCreator ClientUri[{}] Error ErrMsg:[{}]", uri, errorEvent.msg);
                Tio.close(channelContext, "WebsocketCreator Error");
                onDestroy(true);
            }, throwable -> {
                state = WebSocket.CLOSED;
                logger.info("WebsocketCreator WebSocket Exception");
                Utils.ConsoleExecptionLog(throwable);
                Tio.close(channelContext, "WebsocketCreator Exception");
                onDestroy(true);
            }));
            webSocket = wsClient.connect();
        } catch (Exception e) {
            state = WebSocket.CLOSED;
            Utils.ConsoleExecptionLog(e);
            Tio.close(channelContext, "WebsocketCreator Exception");
            onDestroy(true);
        }
    }

    /**
     * 定时发送心跳包
     *
     * @return
     */
    private void sendHeartbeatPacket() {
        if (null == timer) {
            timer = new Timer();
        }
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                if (webSocket != null && state == WebSocket.OPEN) {
                    webSocket.send(Constants.HEARTBEAT_PACKET_JSON_STRING);
                }
            }
        }, 10000, 30000);
    }

    /**
     * 推送消息
     *
     * @param message
     */
    public void pushMessage(Object message) {
        if (null != webSocket && message != null) {
            if (message instanceof String) {
                webSocket.send((String) message);
            } else if (message instanceof byte[]) {
                webSocket.send((byte[]) message);
            } else {
                logger.error("UNKOWN PushMessage [{}] Type", message.getClass().getName());
            }
        }
    }

    /**
     * 关闭连接
     */
    public void onDestroy(boolean cleanCache) {
        logger.warn("ClusterClient [{}] Closed Success!", uri);
        //缓存中的对象也需要删除
        if (cleanCache) {
            WebSocketClientCache.getInstance().deleteCache(uri);
        }
        if (null != timer) {
            timer.cancel();
            timer = null;
        }
        if (null != webSocket) {
            webSocket.close();
            this.webSocket = null;
        }
        if (null != wsClient) {
            wsClient.close();
            wsClient = null;
        }
        this.channelContext = null;
        this.uri = null;
    }

    public String getUri() {
        return uri;
    }

    public ChannelContext getChannelContext() {
        return channelContext;
    }

    public int getState() {
        return state;
    }

    public WebSocket getWebSocket() {
        return webSocket;
    }
}
